//
// Created by hsj on 2024-1-9.
// Windows没有fork
// 杀死僵尸进程
// ****************************************************僵尸进程定义***************************************************
// “应该向创建子进程的父进程传递子进程的exit参数值或return语句的返回值。”
// 如何向父进程传递这些值呢?操作系统不会主动把这些值传递给父进程。
// 只有父进程主动发起请求(函数调用)时，操作系统才会传递该值。
// 换言之，如果父进程未主动要求获得子进程的结束状态值，操作系统将-直保存， 并让子进程长时间处于僵尸进程状态。
// 也就是说，父母要负责收回自己生的孩子(也许这种描述有些不妥)。
// ****************************************************僵尸进程定义[结束]***************************************************
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>
//防止僵尸进程，
void read_childproc(int sig);
int main(void)
{
    int i = 0;
    printf("i son/pa ppid  pid   fpid\n");

    /**
     * struct sigaction
       {
            void (*sa_handler)(int);
            sigset_t sa_mask;
            int sa_flags;
       }
        此结构体的sa_handler成 员保存信号处理函数的指针值(地址值)。
        sa_mask和sa_flags的所有位均初始化为0即可。这2个成员用于指定信号相关的选项和特性，而我们的目的主要是防止产生僵尸进程，故省略。

     */
    struct sigaction act;
    act.sa_handler=read_childproc;
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;
    //注册子进程结束事件
    sigaction(SIGCHLD,&act, 0);

    //ppid指当前进程的父进程pid
    //pid指当前进程的pid,
    //fpid指fork返回给当前进程的值
    for (i = 0; i < 2; i++)
    {
        pid_t fpid = fork();
        if (fpid == 0)
        {
            printf("%d child  %4d %4d %4d\n", i, getppid(), getpid(), fpid);
            return 0;
        }
        else
        {
            printf("%d parent %4d %4d %4d\n", i, getppid(), getpid(), fpid);
        }
//        sleep(3); //3 sec
    }
    return 0;
}
void read_childproc(int sig){
    pid_t pid;
    int status;
    //非阻塞式退出进程
    //waitpid第一个参数：等待终止的目标子进程的ID，若传递-1，则与wait函数相同，可以等待任意子进程终止。
    pid=waitpid(-1, &status, WNOHANG);
    if(WIFEXITED(status)){//检测退出了吗
        printf("signal: Removed Child proc  id: %d \n", pid);
        printf("signal: Child proc return/back value: %d \n", WEXITSTATUS(status));//获取退出的子进程的退出码/返回值
    }
}